#include "animation_set.hpp"
#include <sstream>

#include "debug.hpp"

std::string BaseAnimation::MakePath(const std::string& base, const std::string& name, const std::string& ext)
{
	std::string image_path = base + name + ext;
	if (name.at(0) == '/') // full path from game root
	{
		std::string doubleShash = "//";
		std::string::size_type pos2Slash = image_path.find(doubleShash.c_str(), 0, doubleShash.size());
		// должен быть дубляж строк, и первую его половину нужно найти что бы удалить
		std::string::size_type pos2SlashEnd = image_path.find("/", pos2Slash + doubleShash.size(), 1);
		std::string folderName = image_path.substr(pos2Slash + doubleShash.size(), pos2SlashEnd - pos2Slash - doubleShash.size());
		std::string::size_type pos1Dup = image_path.find(folderName, 0);
		std::string new_image_path = image_path.replace(pos1Dup, pos2Slash - pos1Dup + doubleShash.size(), "");
		image_path = new_image_path;
	}
	return image_path;
}

AnimationSet::AnimationSet(): m_current_alpha(1.0f), m_current_angle(0.f), m_looped(false)
{
}

AnimationSet::~AnimationSet()
{
}

void AnimationSet::Initialize(pugi::xml_node& anim_set_node, const std::string& base_dir)
{
	Clear();
	m_base_dir = base_dir;
	m_looped = anim_set_node.attribute("looped").as_bool();
	for (pugi::xml_node anim_node = anim_set_node.child("animation"); anim_node; anim_node = anim_node.next_sibling("animation"))
	{
		std::string applier = anim_node.attribute("applier").value();
		if(applier == "picture")
		{
			boost::shared_ptr<ImageAnimation> animation(new ImageAnimation(*this));
			animation->Initialize(anim_node, base_dir);
			m_animations.push_back(animation);
		} else if (applier == "position")
		{
			boost::shared_ptr<PositionAnimation> animation(new PositionAnimation(*this));
			animation->Initialize(anim_node, base_dir);
			m_animations.push_back(animation);
		} else if (applier == "alpha")
		{
			boost::shared_ptr<AlphaAnimation> animation(new AlphaAnimation(*this));
			animation->Initialize(anim_node, base_dir);
			m_animations.push_back(animation);
		}
		else if (applier == "rotationZ")
		{
			boost::shared_ptr<RotationZAnimation> animation(new RotationZAnimation(*this));
			animation->Initialize(anim_node, base_dir);
			m_animations.push_back(animation);
		}
		else
		{
			fire_exception("can't find animation for applier: %s", applier.c_str());
		}
	}
}

ImageAnimation::ImageAnimation(AnimationSet& anim_set): m_current_image(0),
 m_time_from_start(0.f), m_anim_set(anim_set)
{
}
/* exapmle xml file animset
<animation_set looped="1">
	<animation type="keys" applier="picture" priority="0" weight="1.0" start="0.0" looped="1">
		<keys type="picture">
			<cyclic value="0" />
			<picture>
				<key relative_time="0.0">bbq_0001</key>
				<key relative_time="0.04">bbq_0002</key>
				<key relative_time="0.04">bbq_0003</key>
				<key relative_time="0.04">bbq_0004</key>
				<key relative_time="0.04">bbq_0005</key>
				<key relative_time="0.04">bbq_0006</key>
				<key relative_time="0.04">bbq_0007</key>
				<key relative_time="0.04">bbq_0008</key>
				<key relative_time="0.04">bbq_0009</key>
				<key relative_time="0.04">bbq_0010</key>
			</picture>
		</keys>
	</animation>
</animation_set>
 */
void ImageAnimation::Initialize(pugi::xml_node& anim_node, const std::string& base_dir)
{
	pugi::xml_node animation = anim_node;
	std::stringstream ss;
	ss << "animation type = " << animation.attribute("type").value() << "\n";
	pugi::xml_node keys = animation.child("keys");
	ss << "keys type = " << keys.attribute("type").value() << "\n";
	pugi::xml_node cyclic = keys.child("cyclic");
	ss << "cyclic value = " << cyclic.attribute("value").as_int() << "\n";
	pugi::xml_node picture = keys.child("picture");
	for (pugi::xml_node key = picture.child("key"); key; key = key.next_sibling("key"))
	{
		ss << "key relative_time = " << key.attribute("relative_time").as_float() << " img path = " << key.child_value() << "\n";
		ImagePtr image(new sf::Image());
		std::string image_path = BaseAnimation::MakePath(base_dir, key.child_value(), ".png"); // base_dir + key.child_value() + ".png";
		if (!image->LoadFromFile(image_path))
		{
			fire_exception("can't load image file from: %s", image_path.c_str());
		}
		float relative_time = key.attribute("relative_time").as_float();
		TimeAndImage timeImage(relative_time, image);
		m_images.push_back(timeImage);
		
	}
}

ImageAnimation::~ImageAnimation()
{
}

void ImageAnimation::Update(float dt)
{
	m_time_from_start += dt;
	int next_index = (m_current_image + 1) >= (int)m_images.size() ? m_current_image : m_current_image + 1;
	float next_relative_time_image = m_images.at(next_index).first;
	if (m_time_from_start >= next_relative_time_image)
	{
		m_time_from_start -= next_relative_time_image;
		m_current_image++;
		m_current_image %= m_images.size();
	}
	ImagePtr image = m_images.at(m_current_image).second;
	m_anim_set.SetImage(image);
}

void AnimationSet::Render(sf::RenderWindow& window) const
{
	sf::Sprite sprite;
	sprite.SetPosition(m_currentPos.x, m_currentPos.y);
	sprite.SetImage(*m_currentImage.get());
	sprite.SetScale(1.f, 1.f);
	sprite.SetRotation(0.f);
	sf::Color col = sprite.GetColor();
	col.a = (sf::Uint8)255 * m_current_alpha;
	sprite.SetColor(col);
	sprite.SetRotation(m_current_angle);
	window.Draw(sprite);
}

AlphaAnimation::AlphaAnimation(AnimationSet& anim_set): m_current_pos(0),
	m_time_from_start(0.f), m_anim_set(anim_set)
{
}

AlphaAnimation::~AlphaAnimation()
{
	
}
	
void AlphaAnimation::Update(float dt)
{
	m_time_from_start += dt;
	int next_index = (m_current_pos + 1) >= (int)m_alpha_values.size() ? m_current_pos : m_current_pos + 1;
	float next_relative_time_image = m_alpha_values.at(next_index).first;
	if (m_time_from_start >= next_relative_time_image)
	{
		m_time_from_start -= next_relative_time_image;
		m_current_pos++;
		m_current_pos %= m_alpha_values.size();
	}
	// liner interp
	float delta = 0.f;
	float newAlpha = m_alpha_values.at(m_current_pos).second;
	if (next_relative_time_image != 0.f)
	{
		delta = m_time_from_start / next_relative_time_image;	
		newAlpha = (1.f - delta) * m_alpha_values.at(m_current_pos).second + delta * m_alpha_values.at(next_index).second;
	}
	m_anim_set.SetAlpha(newAlpha);
}

//<animation type="keys" applier="alpha" priority="0" weight="1.0" start="0.0" looped="0">
//  <keys type="float">
//   <cyclic value="0" interp="linear"/>
//   <float>
//    <key relative_time="0.2">0.0</key>
//    <key relative_time="0.2">0.5</key>
//    <key relative_time="0.2">1.0</key>
//    <key relative_time="0.2">0.5</key>
//    <key relative_time="0.2">0.0</key>
//   </float>
//  </keys>
// </animation>

void AlphaAnimation::Initialize(pugi::xml_node& anim_node, const std::string& base_dir)
{
	pugi::xml_node animation = anim_node;
	std::stringstream ss;
	ss << "animation type = " << animation.attribute("type").value() << "\n";
	pugi::xml_node keys = animation.child("keys");
	ss << "keys type = " << keys.attribute("type").value() << "\n";
	pugi::xml_node cyclic = keys.child("cyclic");
	ss << "cyclic value = " << cyclic.attribute("value").as_int() << "\n";
	pugi::xml_node float_node = keys.child("float");
	for (pugi::xml_node key = float_node.child("key"); key; key = key.next_sibling("key"))
	{
		ss << "key relative_time = " << key.attribute("relative_time").as_float() << " vec val = " << key.child_value() << "\n";
		float pos;
		sscanf(key.child_value(), "%f", &pos);
		float relative_time = key.attribute("relative_time").as_float();
		TimeAndAlpha timeFloat(relative_time, pos);
		m_alpha_values.push_back(timeFloat);
	}
}

void AnimationSet::Clear()
{
	m_animations.clear();
	m_base_dir.clear();
	m_currentImage.reset();
}

void AnimationSet::Update(float dt)
{
	for (int i = 0; i < (int)m_animations.size(); i++)
	{
		m_animations.at(i)->Update(dt);
	}
}
/*
<animation type="keys" applier="position" priority="0" weight="1.0" start="0.0" looped="0">
		<keys type="vector3" interp="linear">
			<cyclic value="0" />
			<vector3>
				<key relative_time="0.0">328 -200 0</key>
				<key relative_time="1.4">328 -200 0</key>
				<key relative_time="0.01">328 -140 0</key>
				<key relative_time="0.01">328 -90 0</key>
				<key relative_time="0.01">328 -40 0</key>
				<key relative_time="0.01">328 10 0</key>
				<key relative_time="0.01">328 60 0</key>
				<key relative_time="0.01">328 110 0</key>
				<key relative_time="0.01">328 160 0</key>
				<key relative_time="0.01">328 210 0</key>
				<key relative_time="0.01">328 260 0</key>
				<key relative_time="0.01">328 310 0</key>
				<key relative_time="0.01">328 360 0</key>
				<key relative_time="0.03">315 360 0</key>
				<key relative_time="0.03">335 345 0</key>
				<key relative_time="0.03">328 330 0</key>
				<key relative_time="0.03">300 340 0</key>
				<key relative_time="0.02">328 353 0</key>
			</vector3>
		</keys>
	</animation> 
 */
void PositionAnimation::Initialize(pugi::xml_node& anim_node, const std::string& base_dir)
{
	pugi::xml_node animation = anim_node;
	std::stringstream ss;
	ss << "animation type = " << animation.attribute("type").value() << "\n";
	pugi::xml_node keys = animation.child("keys");
	ss << "keys type = " << keys.attribute("type").value() << "\n";
	pugi::xml_node cyclic = keys.child("cyclic");
	ss << "cyclic value = " << cyclic.attribute("value").as_int() << "\n";
	pugi::xml_node vector3 = keys.child("vector3");
	for (pugi::xml_node key = vector3.child("key"); key; key = key.next_sibling("key"))
	{
		ss << "key relative_time = " << key.attribute("relative_time").as_float() << " vec val = " << key.child_value() << "\n";
		sf::Vector3f pos;
		sscanf(key.child_value(), "%f %f %f", &pos.x, &pos.y, &pos.z);
		float relative_time = key.attribute("relative_time").as_float();
		TimeAndVec3 timeVec3(relative_time, pos);
		m_positions.push_back(timeVec3);
	}
}

PositionAnimation::PositionAnimation(AnimationSet& anim_set): m_current_pos(0),
 m_time_from_start(0.f), m_anim_set(anim_set)
{
}

void PositionAnimation::Update(float dt)
{
	m_time_from_start += dt;
	int next_index = (m_current_pos + 1) >= (int)m_positions.size() ? m_current_pos : m_current_pos + 1;
	float next_relative_time_image = m_positions.at(next_index).first;
	if (m_time_from_start >= next_relative_time_image)
	{
		m_time_from_start -= next_relative_time_image;
		m_current_pos++;
		m_current_pos %= m_positions.size();
	}
	// liner interp
	float delta = 0.f;
	sf::Vector3f pos = m_positions.at(m_current_pos).second;
	if (next_relative_time_image != 0.f)
	{
		delta = m_time_from_start / next_relative_time_image;	
		pos = (1.f - delta) * m_positions.at(m_current_pos).second + delta * m_positions.at(next_index).second;
	}
	//sf::Vector3f& pos = m_positions.at(m_current_pos).second;
	m_anim_set.SetPosition(pos);
}

PositionAnimation::~PositionAnimation()
{
	// empty
}

RotationZAnimation::RotationZAnimation(AnimationSet& anim_set): m_current_pos(0), m_time_from_start(0.f), m_anim_set(anim_set)
{
}

void RotationZAnimation::Initialize(pugi::xml_node& anim_node, const std::string& base_dir)
{
	pugi::xml_node animation = anim_node;
	std::stringstream ss;
	ss << "animation type = " << animation.attribute("type").value() << "\n";
	pugi::xml_node keys = animation.child("keys");
	ss << "keys type = " << keys.attribute("type").value() << "\n";
	pugi::xml_node cyclic = keys.child("cyclic");
	ss << "cyclic value = " << cyclic.attribute("value").as_int() << "\n";
	pugi::xml_node float_node = keys.child("float");
	for (pugi::xml_node key = float_node.child("key"); key; key = key.next_sibling("key"))
	{
		ss << "key relative_time = " << key.attribute("relative_time").as_float() << " vec val = " << key.child_value() << "\n";
		float pos;
		sscanf(key.child_value(), "%f", &pos);
		float relative_time = key.attribute("relative_time").as_float();
		TimeAndAngle time_angle(relative_time, pos);
		m_angle_values.push_back(time_angle);
	}
}

void RotationZAnimation::Update(float dt)
{
	m_time_from_start += dt;
	int next_index = (m_current_pos + 1) >= (int)m_angle_values.size() ? m_current_pos : m_current_pos + 1;
	float next_relative_time_image = m_angle_values.at(next_index).first;
	if (m_time_from_start >= next_relative_time_image)
	{
		m_time_from_start -= next_relative_time_image;
		m_current_pos++;
		m_current_pos %= m_angle_values.size();
	}
	// liner interp
	float delta = 0.f;
	float newAlpha = m_angle_values.at(m_current_pos).second;
	if (next_relative_time_image != 0.f)
	{
		delta = m_time_from_start / next_relative_time_image;	
		newAlpha = (1.f - delta) * m_angle_values.at(m_current_pos).second + delta * m_angle_values.at(next_index).second;
	}
	m_anim_set.SetRotationZ(newAlpha);
}

RotationZAnimation::~RotationZAnimation()
{
}

